package org.hashdb.client.net.nio;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import org.hashdb.client.config.AppConfig;
import org.hashdb.client.net.nio.msg.ActMessage;
import org.hashdb.client.net.nio.msg.ErrorMessage;
import org.hashdb.client.net.nio.msg.Message;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * Date: 2024/1/18 18:35
 *
 * @author huanyuMake-pecdle
 * @version 0.0.1
 */

public class V1HashProtocolCodec implements ProtocolCodec {

    @Override
    public @NotNull ByteBuf encode(ChannelHandlerContext ctx, Message<?> msg) {
        var body = msg.getMeta().bodyParser().encode(msg.body());
        var out = ctx.alloc().buffer(body.length + 30);
        // 4 magic
        out.writeBytes(CodecDispatcher.MAGIC);
        // 1 version
        out.writeByte(Protocol.HASH_V1.key());
        // 1 deserialize method 其实这个字段可以不记录, 只需要知道消息体类型, 即type字段就知道如何解析了
        // 但是如果去掉这个byte,那么消息头的大小就变成了21,还需要padding补齐,所以干脆就这样的了
        out.writeByte(msg.getMeta().bodyParser().key());
        // 4 type
        out.writeInt(msg.getMeta().key());
        // 8 message id
        out.writeLong(msg.id());
        // 4 body length
        // 如果是应答类消息, 那么body前8个字节就为actId
        if (msg.getMeta().isActMessage()) {
            // expand body length for actId
            out.writeInt(body.length + 8);
            // 4 act message id
            out.writeLong(((ActMessage<?>) msg).actId());
        } else {
            out.writeInt(body.length);
        }
        // [body length] body
        out.writeBytes(body);
        return out;
    }

    @Override
    public @Nullable Message<?> decode(ChannelHandlerContext ctx, ByteBuf in) {
        // check body parse method
        in.readByte();
        var messageMeta = ProtocolCodec.resolveMessageMeta(ctx, in);
        if (messageMeta == null) {
            return null;
        }
        var id = in.readLong();
        // 读取body长度
        in.readInt();
        // 如果是应答类消息, 那么body前8个字节就为actId
        Long actId = null;
        if (messageMeta.isActMessage()) {
            actId = in.readLong();
        }
        // parse body
        byte[] bodyBytes;
        int offset;
        int length;
        // 这个bodyBytes可能不需要复制,能0拷贝就0拷贝
        var reader = AppConfig.getInstance().getAppContext().reader();
        if (in.hasArray()) {
            reader.messageLn("MESSAGE BODY PARSE FAST");
            offset = in.readerIndex();
            length = in.writerIndex();
            bodyBytes = in.array();
        } else {
            reader.messageLn("MESSAGE BODY PARSE SLOW");
            offset = 0;
            length = in.readableBytes();
            bodyBytes = new byte[length];
            in.readBytes(bodyBytes);
        }
        try {
            return messageMeta.create(id, actId, bodyBytes, offset, length);
        } catch (Exception e) {
            reader.messageLn("MESSAGE PARSE ERROR");
            reader.printStackTrace(e);
            ctx.writeAndFlush(new ErrorMessage(0, "body pase error"));
            return null;
        }
    }
}
